package com.example.artemis.model;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.UUID;

/**
 * Queue message model for ActiveMQ Artemis message queuing
 * Messages are designed to be persistent and Serializable
 */
public class QueueMessage implements Serializable {
    private static final long serialVersionUID = 1L;

    private String id;
    private String content;
    private String producerId;
    private int shardKey;

    private LocalDateTime timestamp;
    private int deliveryAttempt;
    private long processingTimeMs;
    private long processedTime;
    private boolean firstAttemptSuccess = false;
    private int retryCount = 0;

    public QueueMessage() {
        this.timestamp = LocalDateTime.now();
        this.deliveryAttempt = 1;
        this.processingTimeMs = 0;
    }

    public QueueMessage(String content, String producerId) {
        this();
        this.id = UUID.randomUUID().toString();
        this.content = content;
        this.producerId = producerId;
        // Generate shard key from content for consistent distribution
        this.shardKey = Math.abs(content.hashCode());
    }

    /**
     * Calculate which queue this message should go to based on shard key
     * @param numberOfQueues Total number of queues
     * @return Queue index (0-based)
     */
    public int getQueueIndex(int numberOfQueues) {
        return shardKey % numberOfQueues;
    }

    /**
     * Get the queue name for this message
     * @param queuePrefix Queue name prefix
     * @param numberOfQueues Total number of queues
     * @return Full queue name
     */
    public String getQueueName(String queuePrefix, int numberOfQueues) {
        return queuePrefix + getQueueIndex(numberOfQueues);
    }

    // Getters and setters
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
        this.shardKey = Math.abs(content.hashCode());
    }

    public String getProducerId() {
        return producerId;
    }

    public void setProducerId(String producerId) {
        this.producerId = producerId;
    }

    public int getShardKey() {
        return shardKey;
    }

    public void setShardKey(int shardKey) {
        this.shardKey = shardKey;
    }

    public LocalDateTime getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(LocalDateTime timestamp) {
        this.timestamp = timestamp;
    }

    public int getDeliveryAttempt() {
        return deliveryAttempt;
    }

    public void setDeliveryAttempt(int deliveryAttempt) {
        this.deliveryAttempt = deliveryAttempt;
    }

    public long getProcessingTimeMs() {
        return processingTimeMs;
    }

    public void setProcessingTimeMs(long processingTimeMs) {
        this.processingTimeMs = processingTimeMs;
    }

    /**
     * Increment delivery attempt counter
     */
    public void incrementDeliveryAttempt() {
        this.deliveryAttempt++;
        if (this.deliveryAttempt > 1) {
            this.retryCount = this.deliveryAttempt - 1;
        }
    }

    /**
     * Mark first attempt as successful
     */
    public void markFirstAttemptSuccess() {
        if (this.deliveryAttempt == 1) {
            this.firstAttemptSuccess = true;
        }
    }

    /**
     * Check if message was successful on first attempt
     */
    public boolean isFirstAttemptSuccess() {
        return firstAttemptSuccess;
    }

    /**
     * Get retry count (number of retries after first attempt)
     */
    public int getRetryCount() {
        return retryCount;
    }

    /**
     * Get processed time
     */
    public long getProcessedTime() {
        return processedTime;
    }

    /**
     * Set processed time
     */
    public void setProcessedTime(long processedTime) {
        this.processedTime = processedTime;
    }

    /**
     * Set created time
     */
    public void setCreatedTime(long createdTime) {
        this.timestamp = LocalDateTime.now();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        QueueMessage that = (QueueMessage) o;
        return Objects.equals(id, that.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public String toString() {
        return String.format("QueueMessage{id='%s', content='%s', producerId='%s', shardKey=%d, " +
                        "timestamp=%s, deliveryAttempt=%d, processingTimeMs=%d, " +
                        "firstAttemptSuccess=%s, retryCount=%d}",
                id, content, producerId, shardKey, timestamp, deliveryAttempt, processingTimeMs,
                firstAttemptSuccess, retryCount);
    }
}